#define _GNU_SOURCE
#include "app_bt.h"
#include "log/log.h"
#include "string.h"
#include <unistd.h>
#include "app_serial.h"

static int init_bt(Device *device)
{
    // 初始串口 9600 / 阻塞
    app_serial_init(device);

    // 设置串口为非阻塞模式
    app_serial_setBlock(device, 0); 
    tcflush(device->fd, TCIOFLUSH);

    // 判断蓝牙可用才去设置蓝牙属性
    if (app_bt_status(device) == 0)
    {
        // 修改蓝牙属性
        app_bt_rename(device, "atguigu");
        app_bt_setBaudRate(device, BT_115200);
        // 重置
        app_bt_reset(device);
        sleep(2); // 等待蓝牙复位
    }

    // 将串口的波特率修改为115200
    app_serial_setBraudRate(device, BR_115200);
    tcflush(device->fd, TCIOFLUSH);

    app_bt_setNetId(device, "1234"); 
    app_bt_setMAddr(device, "0001");
    
    // 判断蓝牙是否可用， 如果不可用则返回-1
    if (app_bt_status(device) != 0)
    {
        log_error("蓝牙初始化失败");
        return -1;
    }

    // 将串口改为阻塞模式
    app_serial_setBlock(device, 1);
    tcflush(device->fd, TCIOFLUSH);

    log_debug("蓝牙初始化成功");
    return 0;
}

/**
 * 初始化
 */
int app_bt_init(Device *device)
{
    // 将蓝牙数据读后处理函数和写前处理注册到设备中
    device->post_read = app_bt_postRead;
    device->pre_write = app_bt_preWrite;

    // 初始化蓝牙
    return init_bt(device);
}

static char read_buf[1024]; // 用来缓存多个蓝牙数据包
static int buf_len = 0; // 缓存数据的长度
static char fix_header[2] = {0xf1, 0xdd}; // 蓝牙数据包的固定头部

/**
 * 移除缓存容器前面指定长度的无用数据
 */
void remove_data(int len)
{
    memmove(read_buf, read_buf+len, buf_len-len);
    buf_len -= len;
}

/**
 * 蓝牙数据的读后处理函数
接收方得到数据1（4）：ok\r\n
接收方得到数据2（3 + [2]）：f1 dd 07 23 23 ff ff 41 42 43
    f1 dd : 固定的头部
    07： 之后数据的长度（5-16之间） 
    23 23：对端（发送方）的MADDR
    ff ff: 我的MADDR或ffff(群发)
    41 42 43：发送的数据 
处理后的数据格式：conn_type id_len msg_len id msg

    data很可能不是一个完整的蓝牙数据包，需要将多个data拼接起来，然后找出一个完整的蓝牙数据包
 */
int app_bt_postRead(char *data, int len)
{
    log_debug("接收到一份蓝牙数据: %.*s", len, data);
    
    // 将data添加到缓存容器中
    memcpy(read_buf+buf_len, data, len);
    buf_len += len;
    // 如果缓存数据长度小于最小的蓝牙数据包长度8，则返回
    if (buf_len < 8)
    {
        return 0;
    }
    // 遍历缓存数据， 找到第一个完整的蓝牙数据包
    int i;
    for(i=0;i<buf_len-7;i++)
    {
        // 如果找到f1dd, 这一段就可能是一个完整的蓝牙数据包
        if(memcmp(read_buf+i, fix_header, 2) == 0)
        {
            // 移除前面的无用数据
            if (i>0)
            {
                remove_data(i);
            }
            // 判断后面的数据是否完整, 如果不完整，则返回
            if (3+read_buf[2] > buf_len)
            {
                log_debug("蓝牙数据还不完整，等待继续读取");
                return 0;
            }
            // 根据蓝牙数据包，生成字符数组保存到data中
            memset(data, 0, len);
            // conn_type
            data[0] = 1;
            // id_len
            data[1] = 2;
            // msg_len
            data[2] = read_buf[2] - 4;
            // id
            memcpy(data+3, read_buf+3, 2);
            // msg
            memcpy(data+5, read_buf+7, data[2]);

            // 移除缓存容器中蓝牙数据包
            remove_data(3 + read_buf[2]);

            // 返回字符数组消息长度
            return 3 + 2 + data[2];
        }
    }
    // 遍历的都不是蓝牙数据包数据，删除遍历的无用数据
    remove_data(i);

    return 0;
}

/**
 * 蓝牙数据的写前处理函数
字符数组消息: 
    例子：1 2 3 XX abc
    格式：conn_type id_len msg_len id msg
蓝牙发送数据格式例子：
    例子：41 54 2b 4d 45 53 48 00 ff ff 61 62 63 0d 0a
    41 54 2b 4d 45 53 48 00： AT+MESH（固定头部）
    ff ff: 对端的MADDR（如果是FFFF代表群发）
    61 62 63: 要发送的数据（不超过12字节）
    0d 0a：\r\n（固定结尾）
 */
int app_bt_preWrite(char *data, int len)
{
    // 1 2 3 XX abc
    // 41 54 2b 4d 45 53 48 00 ff ff 61 62 63 0d 0a
    // 检查数据长度
    if (len < 6)
    {
        log_error("data不能小于6字节");
        return -1;
    }

    // 计算出蓝牙数据的长度，并创建一个缓存容器
    int blue_len = 12 + data[2];
    char blue_buf[blue_len];

    // 将蓝牙各个部分的数据写入缓存容器
    // AT+MESH（固定头部）
    memcpy(blue_buf, "AT+MESH", 8);
    // id
    memcpy(blue_buf+8, data+3, 2);
    // msg
    memcpy(blue_buf+10, data+5, data[2]);
    // \r\n（固定结尾）
    memcpy(blue_buf+10+data[2], "\r\n", 2);

    // 消除data中的数据， 将缓存容器中的数据拷贝到data中
    memset(data, 0, len);
    memcpy(data, blue_buf, blue_len);

    log_debug("生成蓝牙数据：%.*s", blue_len, data);
    // 返回蓝牙数据的长度
    return blue_len;
}

/**
 * 获取（读）确认（成功）返回指令(OK\r\n)数据， 如果得到返回0, 否则返回-1
 */
static int wait_ack(Device *device)
{
    // 等待一定的时间
    usleep(50 * 1000);
    // 读取串口数据
    char buf[4];
    read(device->fd, buf, sizeof(buf));
    // 判断是否是OK\r\n
    if (memcmp(buf, "OK\r\n", 4) == 0)
    {
        return 0;
    }
    return -1;
}

/**
 * 测试蓝牙是否可用
 */
int app_bt_status(Device *device)
{
    // 向串口发送（写）AT指令
    write(device->fd, "AT\r\n", 4);

    // 等待获取ACK
    return wait_ack(device);
}

/**
 * 修改蓝牙名称
 */
int app_bt_rename(Device *device, char *name)
{
    char buf[100];
    sprintf(buf, "AT+NAME%s\r\n", name);

    // 向串口发送（写）AT指令
    write(device->fd, buf, strlen(buf));

    // 等待获取ACK
    return wait_ack(device);
}

/**
 * 设置波特率
 */
int app_bt_setBaudRate(Device *device, BTBraudRate braud_rate)
{
    char buf[100];
    sprintf(buf, "AT+BAUD%c\r\n", braud_rate);

    // 向串口发送（写）AT指令
    write(device->fd, buf, strlen(buf));

    // 等待获取ACK
    return wait_ack(device);
}

/**
 * 启动蓝牙
 */
int app_bt_reset(Device *device)
{
    // 向串口发送（写）AT指令
    write(device->fd, "AT+RESET\r\n", 10);

    // 等待获取ACK
    return wait_ack(device);
}

/**
 * 设置组网id
 * net_id: 4位16进制字符串
 */
int app_bt_setNetId(Device *device, char *net_id)
{
    char buf[100];
    sprintf(buf, "AT+NETID%s\r\n", net_id);

    // 向串口发送（写）AT指令
    write(device->fd, buf, strlen(buf));

    // 等待获取ACK
    return wait_ack(device);
}

/**
 * 设置MAC地址
 * maddr: 4位16进制字符串
 */
int app_bt_setMAddr(Device *device, char *maddr)
{
    char buf[100];
    sprintf(buf, "AT+MADDR%s\r\n", maddr);

    // 向串口发送（写）AT指令
    write(device->fd, buf, strlen(buf));

    // 等待获取ACK
    return wait_ack(device);
}
